﻿/*
 * Authors:    Kęstutis Džiautas
 *             Edmundas Matusevičius
 * University: VU, MIF, SE, 4 course
 * Date:       2010-11-20
 * Version:    1.0
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

using LexAnalizer;

namespace SyntaxAnalizer {
    class SyntaxAnalizer {
        public SyntaxAnalizer(string codeFileName) {
            lexemAnalizer = new LexemAnalizer(codeFileName);
            grammarStack = new Stack<string>();
            parseTable = new ParseTable();
            resultTree = createFile(resultFName);
        }

        /// <summary>
        /// Analyze lexems.
        /// </summary>
        public void analize() {
            //StreamWriter resultFileWriter = new StreamWriter("results.txt");

            // lexem information variables
            int lexNum;
            string lexInf;

            // construct grammar graph
            Graph grammarGraph = new Graph();

            // push grammar root node declaration to stack
            grammarStack.Push(grammarGraph.getRootNodeDeclaration());

            do {
                // clear variables
                lexNum = 0;
                lexInf = "";

                // get next lexem information
                lexemAnalizer.getLexem(ref lexNum, ref lexInf, lexemAnalizer.getStatesTree().getNodeByState("start"));

                // deduce lexem
                bool deduced = false;
                while (!deduced) {
                    deduced = deduce(lexNum, lexInf);
                }

                //resultFileWriter.WriteLine(String.Format("{0,18:000}", lexNum) + " | " + lexInf);
            } while (lexNum != 0 && !lexInf.Equals(Constants.goodEndValue));

            //resultFileWriter.Close();
            resultTree.Close(); //Closes handle

            Console.WriteLine("ANALYZED");
            Console.ReadLine();
        }

        /// <summary>
        /// Deduce lexem in grammar.
        /// </summary>
        /// <param name="lexNum">Lexem's number.</param>
        /// <param name="lexInf">Laxem.</param>
        /// <returns>True if lexem is deduced, otherwise False.</returns>
        private bool deduce(int lexNum, string lexInf) {
            ArrayList definitionList = new ArrayList();

            // check if stack is not empty
            if (grammarStack.Count != 0) {

                // if nullable remove stack top
                if (grammarStack.Peek().Equals("#")) {
                    grammarStack.Pop();
                }

                // printing code tree
                string stackTop = grammarStack.Peek();
                writeTree(String.Format("{0," + ((grammarStack.Count * 2) + stackTop.Length).ToString() + "}", stackTop));
                switch (stackTop) {
                    case "<Id>":
                    case "<Dec>":
                    case "<Float>":
                    case "<String Def>":
                    case "<Char Def>":
                        writeTree(String.Format("{0," + (((grammarStack.Count + 1) * 2) + lexInf.Length).ToString() + "}", lexInf));
                        break;
                }

                lexInf = "'" + lexInf + "'";

                # region lexNum
                switch (lexNum) {
                    case 0:
                        if (lexInf.Equals("'" + Constants.goodEndValue + "'")) {
                            lexInf = Constants.endValue;
                        } else {
                            Console.WriteLine("ERROR " + lexInf + " ");
                            return true;
                        }
                        break;
                    case 100:
                        lexInf = Constants.idValue;
                        break;
                    case 200:
                        lexInf = Constants.charValue;
                        break;
                    case 201:
                        lexInf = Constants.stringValue;
                        break;
                    case 202:
                        lexInf = Constants.decValue;
                        break;
                    case 203:
                        lexInf = Constants.floatValue;
                        break;
                }
                # endregion

                // if deduced finish, else get definition
                if (grammarStack.Peek().Equals(lexInf)) {
                    grammarStack.Pop();
                    return true;
                } else {
                    definitionList = parseTable.getCellData(grammarStack.Peek(), lexInf);
                }

                // if no definition found error, else change stack top
                if (definitionList == null) {
                    Console.Write("ERROR ");
                    Console.Write("Expected: " + grammarStack.Peek() + " ");
                    Console.WriteLine("Before: " + lexInf);
                    grammarStack.Pop();
                } else {
                    grammarStack.Pop();
                    for (int i = definitionList.Count - 1; i >= 0; i--) {
                        grammarStack.Push((string)definitionList[i]);
                    }
                }

                return false;
            } else {
                return true;
            }
        }

        /// <summary>
        /// Create a handle for the result file
        /// </summary>
        /// <param name="name"> File name</param>
        /// <returns>The handle of the result file where the program tree has to be printed</returns>
        private StreamWriter createFile(string name)
        {
            return new StreamWriter(name);
        }

        /// <summary>
        /// Method to write a program code with grammar tags to file
        /// </summary>
        /// <param name="tag">The tag to be printed in the tree</param>
        private void writeTree(String tag)
        {
            resultTree.WriteLine(tag);
        }

        /// <summary>
        /// Main method only for the purpose of testing.
        /// </summary>
        static void Main(string[] args) {
            //checks if there is enough arguments
            if (args.Length >= 1) {
                // args[0] - code example file name
                if (File.Exists(args[0])) {
                    SyntaxAnalizer syntaxAnalizer = new SyntaxAnalizer(args[0]);
                    // analize code syntactically
                    syntaxAnalizer.analize();
                } else {
                    Console.WriteLine("Code file does not exist.");
                    Console.ReadLine();
                }
            } else {
                Console.WriteLine("Please enter code file name as first argument.");
                Console.ReadLine();
            }
            
        }

        /// <summary>
        /// Grammar stack.
        /// </summary>
        private Stack<string> grammarStack;
        /// <summary>
        /// Parse table.
        /// </summary>
        private ParseTable parseTable;
        /// <summary>
        /// Lexem analizer.
        /// </summary>
        private LexemAnalizer lexemAnalizer;
        /// <summary>
        /// The handel of a file the program tree to be printed. Remember to close it.
        /// </summary>
        private StreamWriter resultTree;
        /// <summary>
        /// The name of a file where the program tree is printed
        /// </summary>
        private string resultFName = "resultTree.txt";
    }
}
